﻿/* Copyright (C) Siemens AG, 2012. All Rights Reserved.
 * Author: Roland Eckl, Corporate Technology
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using System;
using System.Windows.Input;


namespace RVM.Infrastructure
{
    /// <summary>
    /// RemotableCommand is used internally to create commands.
    /// It differs from typical command implementations by having a reference to it's associated viewmodel,
    /// which allows to execute the remote action instead of a local delegate.
    /// </summary>
    internal class RemotableCommand : ICommand
    {
        #region Private Members

        /// <summary>
        /// Actions name. (Property)
        /// </summary>
        private string _name = string.Empty;

        /// <summary>
        /// Associated action to call when executing action.
        /// </summary>
        private Action<object> _execute = null;

        /// <summary>
        /// Associcated predicate.
        /// </summary>
        private Predicate<object> _predicate = null;

        /// <summary>
        /// RemotableViewModel this command belongs to.
        /// </summary>
        private RemotableViewModel _rvm = null;

        #endregion

        #region Constructors

        /// <summary>
        /// Default Constructor.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="execute"></param>
        /// <param name="canExecute"></param>
        /// <param name="vm"></param>
        public RemotableCommand(string name, Action<object> execute, Predicate<object> canExecute, RemotableViewModel vm) : this(name, execute, canExecute)
        {
            _rvm = vm;
        }

        /// <summary>
        /// Constructor for local usage.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="execute"></param>
        /// <param name="canExecute"></param>
        public RemotableCommand(string name, Action<object> execute, Predicate<object> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("No Execute method was set for RemotableCommand.");

            _name = name;
            _execute = execute;
            _predicate = canExecute;
        }

        /// <summary>
        /// Constructor for RVM usage with ICommand instead of pair of action and predicate.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="command"></param>
        /// <param name="vm"></param>
        public RemotableCommand(string name, ICommand command, RemotableViewModel vm)
            : this(name, command)
        {
            _rvm = vm;
        }

        /// <summary>
        /// Constructor with ICommand for local usage.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="command"></param>
        public RemotableCommand(string name, ICommand command)
        {
            _name = name;
            _execute = command.Execute;
            _predicate = command.CanExecute;
            // TODO: Wire up CanExecuteChanged event?
        }

        #endregion

        #region Public Members

        /// <summary>
        /// Event raised whenever the predicate changes.
        /// </summary>
        public event EventHandler CanExecuteChanged
        {
            add
            {
                if (_predicate != null)
                    CommandManager.RequerySuggested += value;
            }
            remove
            {
                if (_predicate != null)
                    CommandManager.RequerySuggested -= value;
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Execute method, invokes remote or local action.
        /// </summary>
        /// <param name="parameter"></param>
        public void Execute(object parameter)
        {
            if((_rvm != null) && (_rvm.Location == ViewModelLocation.Remote))
                _rvm.ExecuteCommand(_name, parameter);
            else
                _execute(parameter);
        }

        /// <summary>
        /// Checks the remote or local predicate.
        /// </summary>
        /// <param name="parameter"></param>
        /// <returns></returns>
        public bool CanExecute(object parameter)
        {
            if ((_rvm != null) && (_rvm.Location == ViewModelLocation.Remote))
                return _rvm.CanExecuteCommand(_name, parameter);
            return _predicate == null ? true : _predicate(parameter);
        }

        #endregion
    }
}
